home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 06 - 1990 / 06.07 Jul 90 / Data Exchange ƒ / Listing7.T < prev    next >
Encoding:
Text File  |  1989-04-24  |  15.1 KB  |  670 lines  |  [TEXT/MPS ]

  1. MODULE FixPObj;
  2. (*
  3.    Go through an object code file and change dictionary occurrences of one 
  4. string to another string.  The default behavior is to change "QUICKDRAW" to
  5. "QuickDraw__Globals" which makes code generated by the Pascal compiler 
  6. compatible with code generated by the Modula compiler.
  7.    This utility can handle MPW object file formats 1 through 3 (MPW 2.0 and
  8. 3.0).  If it is used on a later object file it will emit a warning message.
  9.  
  10.    Arguments:
  11.               --Input file name is required.
  12.                  --Output file name may be specified with '-o' option, otherwise
  13.                    a default output name of FixP.o will be used. The output file
  14.                     name must be different from the input file name.
  15.                  --Strings for substitution may be specified with the '-s' option.
  16.                     i.e., "-s QUICKDRAW=QuickDraw__Globals" would specify the default
  17.                     behavior.
  18.                   
  19.       9/20/88
  20.            --Written by John N. Calley   
  21.         4/24/89 JNC 
  22.            --Updated for MPW 3.0 object file formats
  23.             --Fixed CAP bug that prevented recognition of options
  24.             --Added spinning MPW cursor
  25.         
  26. *)
  27. FROM CursorControl IMPORT
  28.    (*procs*) SpinCursor;
  29. FROM Diagnostic IMPORT
  30.    (*procs*) WriteString, WriteCard, WriteLongInt, WriteInt,
  31.               WriteLn;
  32. FROM FileManager IMPORT
  33.    (*types*) FInfo, 
  34.     (*procs*) GetFInfo, SetFInfo;
  35. FROM IntEnv IMPORT
  36.    (*vars *) ArgC, ArgV, Exit;
  37. FROM IntEnvIO IMPORT
  38.    (*const*) InputFD, OutputFD, RDONLY, WRONLY, CREAT, TRUNC,
  39.     (*procs*) ErrNo, open, read, write, close;
  40. FROM MacTypes IMPORT
  41.    (*types*) Str255, StringHandle, OSErr;
  42. FROM MemoryManager IMPORT
  43.    (*procs*) NewHandle, DisposHandle, HLock, HUnlock;
  44. FROM Strings IMPORT
  45.    (*procs*) Length, MakePascalString, Copy, Pos;
  46. FROM SYSTEM IMPORT
  47.    (*types*) ADDRESS,
  48.    (*procs*) VAL, SHIFT, ADR, LONG;
  49. FROM Utilities IMPORT
  50.    (*procs*) Munger;
  51.     
  52. CONST
  53.    defaultOutFile = "FixP.o";
  54.     latestVersion = 3;   (* latest version of Obj file format understood *)
  55.    pp = FALSE;          (* print progress information *)
  56.     
  57. VAR
  58.     inFileName,
  59.     outFileName,
  60.     inString,
  61.     outString
  62.        :Str255;
  63.         
  64.     inFile,       (* File IDs for input and output files *)
  65.     outFile,
  66.     status        (* Status of last read or write operation *)
  67.        :LONGINT;
  68.         
  69.         
  70. PROCEDURE PrintUsage();
  71. (*
  72.    Print usage statement.
  73. *)
  74. BEGIN
  75.    WriteString ("# ");
  76.    WriteString (ArgV^[0]^);
  77.    WriteString (": Bad option or unable to open file.");
  78.     WriteLn();
  79.    WriteString ("# Usage: ");
  80.    WriteString (ArgV^[0]^);
  81.    WriteString (" [-s oldString=newString] [-o outFileName] inFileName");
  82.     WriteLn();
  83. END PrintUsage;
  84.  
  85. PROCEDURE SetOptions():BOOLEAN;
  86. (*
  87.    Set up input file name, optional output file name and optional string substitutions.  Return
  88. TRUE if all options are interpretable, FALSE if there is an unrecognizable option or if no
  89. input file name is given.
  90. *)
  91. VAR
  92.     i, j
  93.        :INTEGER;
  94.    tempLength
  95.       :INTEGER;
  96.     optionsOK
  97.        :BOOLEAN;
  98.     equalPos        (* Position of '=' in substitution option *)
  99.        :INTEGER;
  100. BEGIN
  101. (* Set defaults *)
  102.    inFileName := "";
  103.     outFileName := defaultOutFile;
  104.     inString := "QUICKDRAW";   (* default substitutions *)
  105.     outString := "QuickDraw__Globals";
  106.    optionsOK := TRUE;
  107.     i := 1;
  108.     WHILE i < ArgC DO
  109.       IF ArgV^[i]^[0] = '-' THEN
  110.             IF CAP(ArgV^[i]^[1]) = "O" THEN
  111.                 INC(i);  (* next argument should be output file name *)
  112.                 outFileName := VAL(Str255, ArgV^[i]^);
  113.             ELSIF CAP(ArgV^[i]^[1]) = "S" THEN
  114.                INC(i);  (* next argument shoud be set of substitution strings *)
  115.                 equalPos := Pos ("=", ArgV^[i]^);
  116.                 IF equalPos = -1 THEN
  117.                    optionsOK := FALSE;
  118.                     WriteString ("No = sign");
  119.                 ELSE
  120.                    Copy (ArgV^[i]^, 0, equalPos, inString);
  121.                     Copy (ArgV^[i]^, equalPos + 1, 
  122.                                      VAL(INTEGER, Length(ArgV^[i]^)) - equalPos,
  123.                                      outString);
  124.                 END; (*IF*)
  125.             ELSE (* Unknown '-' option *)
  126.                 optionsOK := FALSE;
  127.                 WriteString ("Unknown -");
  128.             END; (*IF*)
  129.         ELSE (* We assume it is the input file name *)
  130.            inFileName := VAL(Str255, ArgV^[i]^);
  131.        END; (*IF*)
  132.         INC(i);
  133.     END; (*WHILE*)
  134.     RETURN (optionsOK);
  135. END SetOptions;
  136.  
  137. PROCEDURE OpenFiles():BOOLEAN;
  138. (*
  139.    Open the files indicated by <inFileName> and <outFileName>. Return TRUE if the operations
  140. are successful, FALSE otherwise.
  141. *)
  142. VAR
  143.    success
  144.        :BOOLEAN;
  145.  
  146. BEGIN
  147.    success := TRUE;
  148.     IF Length(inFileName) = 0 THEN
  149.        success := FALSE;
  150.     ELSE
  151.        inFile := open (inFileName, RDONLY);
  152.         IF ErrNo() <> 0D THEN
  153.            success := FALSE;
  154.         END; (*IF*)
  155.     END; (*IF*)
  156.    IF Length(outFileName) = 0 THEN
  157.        outFile := OutputFD;   (* Standard output *)
  158.     ELSE
  159.        outFile := open (outFileName, WRONLY + CREAT + TRUNC);
  160.         IF ErrNo() <> 0D THEN
  161.            success := FALSE;
  162.         END; (*IF*)
  163.     END; (*IF*)
  164.     RETURN (success);
  165. END OpenFiles;
  166.  
  167.  
  168. PROCEDURE ReadWord (VAR value:CARDINAL);
  169. (*
  170.    Read the next two characters as a binary value and return that value.
  171. *)
  172. BEGIN
  173.    status := read (inFile, ADR(value), 2D);
  174. END ReadWord;
  175.  
  176. PROCEDURE WriteWord (value:CARDINAL);
  177. (*
  178.    Write out the indicated integer as two consecutive bytes.
  179. *)
  180. BEGIN
  181.    status := write (outFile, ADR(value), 2D);
  182. END WriteWord;
  183.  
  184. PROCEDURE WriteByte (value:INTEGER);
  185. (*
  186.    Write out the low byte of the indicated integer.
  187. *)
  188. BEGIN
  189.    status := write (outFile, ADR(value) + 1D, 1D);
  190. END WriteByte;
  191.  
  192. PROCEDURE ReadByte ():CHAR;
  193. (*
  194.    Read in one byte and return it as a character
  195. *)
  196. VAR
  197.     tempChar
  198.        :CHAR;
  199. BEGIN
  200.    status := read (inFile, ADR(tempChar), 1D);
  201.     RETURN (tempChar);
  202. END ReadByte;
  203.  
  204. PROCEDURE Pass (length:CARDINAL);
  205. (*
  206.    Pass through the indicated number of bytes from input to output. 
  207. *)
  208. VAR
  209.    tempStr
  210.        :Str255;
  211.     tempLength
  212.        :CARDINAL;
  213. BEGIN
  214.    WHILE length > 0 DO
  215.        IF length > 256 THEN
  216.            tempLength := 256;
  217.             DEC(length, 256);
  218.         ELSE
  219.            tempLength := length;
  220.             length := 0;
  221.         END; (*IF*)
  222.         status := read (inFile, ADR(tempStr), LONG(tempLength));
  223.         status := write (outFile, ADR(tempStr), LONG(tempLength));
  224.     END; (*WHILE*)
  225. END Pass;
  226.  
  227. PROCEDURE ReadString (VAR string:Str255; VAR length:INTEGER);
  228. (*
  229.    Read the pascal formatted string from the input and return it in <string>. 
  230. <length> is the length of the returned string.
  231. *)
  232. VAR
  233.     inChar
  234.        :CHAR;
  235. BEGIN
  236.    status := read (inFile, ADR(inChar), 1D);
  237.     length := VAL(INTEGER, inChar);
  238.     status := read (inFile, ADR(string), LONG(length));
  239.     string[length] := VAL(CHAR, 0);  (* null terminate the string *)
  240. END ReadString;
  241.  
  242. PROCEDURE WritePString (string:Str255);
  243. (*
  244.    Write out the indicated string in pascal format.
  245. *)
  246. VAR
  247.    tempStr
  248.        :Str255;
  249. BEGIN
  250.    MakePascalString (string, tempStr);
  251.     status := write (outFile, ADR(tempStr), LONG(VAL(INTEGER, tempStr[0]) + 1));
  252. END WritePString;
  253.  
  254. PROCEDURE ProcessFirst();
  255. (*
  256.    Pass a First record through. Print a warning if the version number is later
  257. that the latest we know about (1).
  258. *)
  259. VAR
  260.     version
  261.        :CARDINAL;
  262. BEGIN
  263.    IF pp THEN
  264.         WriteString ("First");
  265.         WriteLn();
  266.     END; (*IF*)
  267.     WriteByte (1);
  268.     Pass (1);
  269.     ReadWord (version);
  270.     WriteWord (version);
  271.     IF version > latestVersion THEN
  272.        WriteString ("# Warning: Unknown object file format version. ");
  273.         WriteLn();
  274.         WriteString ("# Output may not be correct.");
  275.         WriteLn();
  276.     END; (*IF*)
  277. END ProcessFirst;
  278.  
  279. PROCEDURE ProcessLast();
  280. (*
  281.    Pass a Last record through.
  282. *)
  283. BEGIN
  284.    IF pp THEN
  285.         WriteString ("Last");
  286.         WriteLn();
  287.     END; (*IF*)
  288.     WriteByte (2);
  289.     Pass (1); 
  290. END ProcessLast;
  291.  
  292. PROCEDURE ProcessComment();
  293. (*
  294.    Pass a comment record on through.
  295. *)
  296. VAR
  297.     size
  298.        :CARDINAL;
  299. BEGIN
  300.    IF pp THEN
  301.        WriteString ("Comment record");
  302.         WriteLn();
  303.     END; (*IF*)
  304.     WriteByte (3);
  305.     Pass (1);
  306.     ReadWord (size);
  307.     WriteWord (size);
  308.     Pass (size - 4);
  309. END ProcessComment;
  310.  
  311. PROCEDURE ReadDict (dict:StringHandle; length:LONGINT);
  312. (*
  313.    Read <length> bytes from standard input into the handle <dict>.
  314. *)
  315. BEGIN
  316.    HLock (dict);
  317.     status := read (inFile, dict^, length);
  318.    HUnlock (dict);
  319. END ReadDict;
  320.  
  321. PROCEDURE ModifyDict (dict:StringHandle):BOOLEAN;
  322. (*
  323.    Substitute <outString> for the string <inString> in <dict>.  There will not be more
  324. that one occurrence.
  325.    Return TRUE if a replacement was done, FALSE if no replacement occurred.
  326. *)
  327. VAR
  328.    pInString,   (* <inString> and <outString> are modula strings, we actually need *)
  329.     pOutString   (* to replace pascal format strings. *)
  330.        :Str255;
  331.     result
  332.        :LONGINT;
  333. BEGIN
  334.    MakePascalString (inString, pInString);
  335.     MakePascalString (outString, pOutString);
  336.     result := Munger (dict, 2, ADR(pInString), LONG(VAL(INTEGER, pInString[0]) + 1),
  337.                                ADR(pOutString), LONG(VAL(INTEGER, pOutString[0]) + 1));
  338.     IF result > 0D THEN
  339.        RETURN(TRUE);
  340.     ELSE
  341.        RETURN(FALSE);
  342.     END; (*IF*)
  343. END ModifyDict;
  344.  
  345. PROCEDURE WriteDict (dict:StringHandle; length:LONGINT);
  346. (*
  347.    Write <length> bytes from <dict> to standard output.
  348. *)
  349. BEGIN
  350.    HLock (dict);
  351.     status := write (outFile, dict^, length);
  352.    HUnlock (dict);
  353. END WriteDict;
  354.  
  355. PROCEDURE ProcessDict();
  356. (*
  357.    Process a dictionary record. If the record defines the string <inString> then replace it
  358. with the string <outString> and write out the modified dictionary record.  If it does not
  359. contain <inString> write it out unchanged.
  360.  
  361.    Method:
  362.        Find out what the current length of the record is.
  363.         Allocate a handle that is large enough for the record after the string has been changed.
  364.         Read the record into the handle.
  365.         Use Munger to perform the substitution if any.
  366.         Write the potentially modified record back out.
  367. *)
  368. VAR
  369.    inChar
  370.        :CHAR;
  371.     length      (* length of the dictionary record *)
  372.        :CARDINAL;
  373.     wasOdd      (* TRUE if the original dictionary record had an odd length *)
  374.        :BOOLEAN;
  375.     dict
  376.        :StringHandle;
  377. BEGIN
  378.    IF pp THEN
  379.         WriteString ("Dictionary");
  380.         WriteLn();
  381.     END; (*IF*)
  382.    inChar := ReadByte();     (* This byte should always be 0 *)
  383.     ReadWord (length); (* length of the dictionary record *)
  384.     IF pp THEN
  385.         WriteString ("Dictionary length is ");
  386.         WriteCard (length, 4);
  387.         WriteLn();
  388.     END; (*IF*)
  389.     wasOdd := ODD(length);
  390.     dict := NewHandle (length + Length(outString));
  391.     length := length - 4;  (* Compensate for the fact that we have already read 4 bytes of header*)
  392.     ReadDict (dict, length);   (* Read the dictionary into the <dict> handle *)
  393.     IF ModifyDict (dict) THEN
  394.        IF pp THEN
  395.             WriteString ("Changed: Old length = ");
  396.             WriteCard (length, 4);
  397.         END; (*IF*)
  398.        length := length + Length(outString) - Length(inString);
  399.         IF pp THEN
  400.             WriteString ("  outString = '");
  401.             WriteString (outString);
  402.             WriteString ("'  length = '");
  403.             WriteCard (Length(outString), 4);
  404.             WriteString ("  inString = '");
  405.             WriteString (inString);
  406.             WriteString ("'  length = '");
  407.             WriteCard (Length(inString), 4);
  408.             WriteLn();
  409.             WriteString ("  New Dictionary length = ");
  410.             WriteCard (length, 4);
  411.             WriteLn();
  412.         END; (*IF*)
  413.     END;
  414.     WriteByte (4);
  415.     WriteByte (0);
  416.     WriteWord (length + 4);
  417.     WriteDict (dict, length);
  418.     IF NOT (wasOdd = ODD(length)) THEN
  419.        WriteByte (0);   (* Write a pad record *)
  420.     END; (*IF*)
  421.     DisposHandle (dict);
  422. END ProcessDict;
  423.  
  424. PROCEDURE ProcessPad();
  425. (*
  426.    Acknowledge that a padding record has been read.
  427. *)
  428. BEGIN
  429.    WriteByte (0);
  430.    IF pp THEN
  431.         WriteString ("Pad");
  432.         WriteLn();
  433.     END; (*IF*)
  434. END ProcessPad;
  435.  
  436. PROCEDURE ProcessDataModule();
  437. (*
  438.    Pass a data module record on through.
  439. *)
  440. VAR
  441.    moduleID,
  442.     size
  443.        :CARDINAL;
  444. BEGIN
  445.    ReadWord (moduleID);
  446.     WriteWord (moduleID);
  447.     ReadWord (size);
  448.     WriteWord (size);
  449.     IF pp THEN
  450.         WriteString ("Data Module: ");
  451.         WriteCard (moduleID, 4);
  452.         WriteString ("size is ");
  453.         WriteCard (size, 4);
  454.         WriteLn();
  455.     END; (*IF*)
  456. END ProcessDataModule;
  457.  
  458. PROCEDURE ProcessCodeModule();
  459. (*
  460.    Pass a code module record on through.
  461. *)
  462. VAR
  463.    moduleID,
  464.     segID
  465.        :CARDINAL;
  466. BEGIN
  467.    ReadWord (moduleID);
  468.     WriteWord (moduleID);
  469.     ReadWord (segID);
  470.     WriteWord (segID);
  471.     IF pp THEN
  472.         WriteString ("Code Module: ");
  473.         WriteCard (moduleID, 4);
  474.         WriteString (" seg ID:");
  475.         WriteCard (segID, 4);
  476.         WriteLn();
  477.     END; (*IF*)
  478. END ProcessCodeModule;
  479.  
  480. PROCEDURE ProcessModule();
  481. (*
  482.    Pass a module record on through.
  483. *)
  484. VAR
  485.     inChar
  486.         :CHAR;
  487.     flags
  488.         :INTEGER;
  489.     
  490. BEGIN
  491.    WriteByte (5);
  492.    inChar := ReadByte (); (*flags*)
  493.     flags := VAL(INTEGER, inChar);
  494.     WriteByte (flags);
  495.     IF ODD(flags) THEN
  496.        ProcessDataModule();
  497.     ELSE
  498.        ProcessCodeModule();
  499.     END; (*IF*)
  500. END ProcessModule;
  501.     
  502. PROCEDURE ProcessEntryPoint();
  503. (*
  504.    Pass an entry point record on through.
  505. *)
  506. BEGIN
  507.    WriteByte (6);
  508.     Pass (7);
  509.     IF pp THEN
  510.         WriteString ("Entry Point");
  511.         WriteLn();
  512.     END; (*IF*)
  513. END ProcessEntryPoint;
  514.  
  515. PROCEDURE ProcessSize();
  516. (*
  517.    Pass a size record on through.
  518. *)
  519. BEGIN
  520.    WriteByte (7);
  521.     Pass (5);
  522. END ProcessSize;
  523.  
  524. PROCEDURE ProcessContents();
  525. (*
  526.    Pass a contents record on through.
  527. *)
  528. VAR
  529.     size          (* Size of the contents record *)
  530.        :CARDINAL;
  531. BEGIN
  532.    WriteByte (8);
  533.     Pass (1);       (* flags *)
  534.     ReadWord (size);
  535.     WriteWord (size);
  536.     IF pp THEN
  537.         WriteString ("Contents:   size=");
  538.         WriteCard (size, 4);
  539.         WriteLn();
  540.     END; (*IF*)
  541.     Pass (size - 4);
  542. END ProcessContents;
  543.  
  544. PROCEDURE ProcessReference ();
  545. (*
  546.    Pass a reference record on through.
  547. *)
  548. VAR
  549.     size
  550.        :CARDINAL;
  551. BEGIN
  552.    WriteByte (9);
  553.    IF pp THEN
  554.         WriteString ("Reference Record");
  555.         WriteLn();
  556.     END; (*IF*)
  557.     Pass (1);      (* flags *)
  558.     ReadWord (size);
  559.     WriteWord (size);
  560.     Pass (size - 4);
  561. END ProcessReference;
  562.  
  563. PROCEDURE ProcessCReference ();
  564. (*
  565.    Pass a computed reference record on through.
  566. *)
  567. VAR
  568.     size
  569.        :CARDINAL;
  570. BEGIN
  571.    WriteByte (10);
  572.     IF pp THEN
  573.         WriteString ("Computed Reference Record");
  574.         WriteLn();
  575.     END; (*IF*)
  576.     Pass (1);      (* flags *)
  577.     ReadWord (size);
  578.     WriteWord (size);
  579.     Pass (size - 4);
  580. END ProcessCReference;
  581.  
  582. PROCEDURE ProcessSymbolic (type:INTEGER);
  583. (*
  584.    Pass a symbolic record on through to the output.
  585. *)
  586. VAR
  587.    size
  588.        :CARDINAL;
  589. BEGIN
  590.    WriteByte (type);
  591.     IF pp THEN
  592.        WriteString ("Symbolic Record: type ");
  593.         WriteInt (type, 1);
  594.         WriteLn();
  595.     END; (*IF*)
  596.     Pass (1); (* flags *)
  597.     ReadWord (size);
  598.     WriteWord (size);
  599.     Pass (size - 4);  (* Body of record data *)
  600. END ProcessSymbolic;
  601.  
  602. PROCEDURE Dispatch (inChar:CHAR);
  603. (*
  604.    Decide who should process this and dispatch control to them.
  605. *)
  606. VAR
  607.    type
  608.        :INTEGER;
  609.         
  610. BEGIN
  611.    type := VAL(INTEGER, inChar);
  612.    CASE type OF
  613.        0 :ProcessPad();                 |
  614.        1 :ProcessFirst();               |
  615.         2 :ProcessLast();                |
  616.         3 :ProcessComment();             |
  617.         4 :ProcessDict();                |
  618.         5 :ProcessModule();              |
  619.         6 :ProcessEntryPoint();          |
  620.         7 :ProcessSize();                |
  621.         8 :ProcessContents();            |
  622.         9 :ProcessReference();           |
  623.         10:ProcessCReference();          |
  624.     (* Symbolic Records for MPW 3.0 *)
  625.        11..19:ProcessSymbolic(type);    |
  626.         ELSE
  627.            (* 
  628.                This happens when the byte past the last byte of the file is read.
  629.                Ignore it.
  630.             *)
  631.     END; (*CASE*)
  632. END Dispatch;
  633.  
  634. PROCEDURE SetOutFileType();
  635. (*
  636.    We created a text file, we need to make it into an OBJ file so that
  637. the linker will accept it.
  638. *)
  639.  
  640. VAR
  641.    fInfo
  642.        :FInfo;
  643.     err
  644.        :INTEGER;
  645. BEGIN
  646.     err := GetFInfo (outFileName, 0, fInfo);
  647.     IF err = 0 THEN
  648.         fInfo.fdType := 'OBJ ';
  649.         err := SetFInfo (outFileName, 0, fInfo);
  650.     END; (*IF*)
  651.     IF err <> 0 THEN
  652.         WriteString ("# Problem setting output file type to 'OBJ '");
  653.         WriteLn();
  654.     END; (*IF*)
  655. END SetOutFileType;
  656.  
  657. BEGIN (*Main*)
  658.    IF SetOptions() AND OpenFiles() THEN
  659.         REPEAT
  660.            SpinCursor (1);
  661.             Dispatch (ReadByte());
  662.         UNTIL status = 0D;
  663.         SetOutFileType(); 
  664.         Exit (0D);
  665.     ELSE
  666.        PrintUsage();
  667.         Exit (1D);
  668.     END; (*IF*)
  669. END FixPObj.
  670.